题目描述
题目有点长,我就截个图展示了。
解题思路
这跟我之前做的那个旋转数组有相似之处,都是两个有序序列的组合。
JavaScript:leetcode_33. 搜索旋转排序数组(二分法)
看题目限制,肯定又是不能用遍历的O(n)。而且对获取mountain的值有100次的限制。
那么自然就想到了二分法,结合题目,mountain长度为10000那么大概分十几次就完事儿了。基本不会把一百次用完。
我的思路是,用二分法找到mountain的山顶top,将其分为两个有序序列,然后分别用二分法查找。
最终算法时间复杂度为 O(log n)
- findTop 查找山顶top
- findLeftTarget 左序查找target,左序列为升序序列。
- findRightTarget 右序列查找target,右序列为降序序列。(也只是在判断条件上有所区别)
如果不太清楚二分法,那么请先看一下 文末扩展
题解:
1 | /** |
扩展 二分法详解
解释:
算法:当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。
- 基本思想:假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较, 如果当前位置arr[k]值等于key,则查找成功;
- 若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1];
- 若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high],
- 直到找到为止,时间复杂度:O(log(n))
使用方法
- 序列必须是有序的,无序序列无法使用二分法。
- 通过递归查找,直至序列长度缩小到2或者1。
模拟实现
以 nums[1,2,3,4,5]为例,找到数组中值为1的下标
- left为 0,right为 4, 声明函数
find(left,right)- 求出中间点
mid = left + ((right - left) >> 1)。 (>> 为位运算,相当于缩小2倍)- 得到 mid 为 2;判断
nums[2] === 1 ?,若等返回mid,nums[2] 为 3,不等 1 ,进入下一步- 判断
nums[mid] > 1,由于nums[2] ===3 > 1,进入左序列find(0, 2)- 求出中间点
mid = 0+ ((2- 0) >> 1),- 得到mid 为 1;判断
nums[1] === 1 ?,若等返回mid,nums[1] 为 2,不等 1 ,进入下一步- 判断
nums[mid] > 1,由于nums[1] ===2 > 1,进入左序列find(0, 1)- 求出中间点
mid = 0+ ((1 - 0) >> 1),- 得到mid 为 0;判断
nums[0] === 1 ?,若等返回mid,nums[0] 为 1,等 1 ,return mid;
至此得到最后结果 下标为 0;
代码实现
1 | let nums = [1,2,3,4,5] ; |
这种是数组中一定包含target的情况下。如果不确定是否包含,需要在值只剩下1-2个的时候做出判断。
1 | let nums = [1,2,3,4,5] ; |
这样如果不存在返回 -1